#include "Triangle.h"
#include "Ray.h"
#include "Vertex.h"

namespace Math{
    
    Triangle::Triangle(){}
    
    Triangle::Triangle(Vertex v0,Vertex v1,Vertex v2){
		Vector n = v0.getPosition();
        m_V0 = v0;
        m_V1 = v1;
        m_V2 = v2;
        preCalc();
    }
    void Triangle::mixNormals(Triangle& o){/*
        if      (o.m_V0==m_V0){o.m_V0.addNormal(m_Normal);}
        else if (o.m_V0==m_V1){o.m_V0.addNormal(m_Normal);}
        else if (o.m_V0==m_V1){o.m_V0.addNormal(m_Normal);}

        if      (o.m_V1==m_V0){o.m_V1.addNormal(m_Normal);}
        else if (o.m_V1==m_V1){o.m_V1.addNormal(m_Normal);}
        else if (o.m_V1==m_V2){o.m_V1.addNormal(m_Normal);}

        if      (o.m_V2==m_V0){o.m_V2.addNormal(m_Normal);}
        else if (o.m_V2==m_V1){o.m_V2.addNormal(m_Normal);}
        else if (o.m_V2==m_V2){o.m_V2.addNormal(m_Normal);}*/
        
        if      (o.m_V0==m_V0){m_V0.addNormal(o.m_Normal);o.m_V0.addNormal(m_Normal);}
        else if (o.m_V0==m_V1){m_V1.addNormal(o.m_Normal);o.m_V0.addNormal(m_Normal);}
        else if (o.m_V0==m_V1){m_V2.addNormal(o.m_Normal);o.m_V0.addNormal(m_Normal);}

        if      (o.m_V1==m_V0){m_V0.addNormal(o.m_Normal);o.m_V1.addNormal(m_Normal);}
        else if (o.m_V1==m_V1){m_V1.addNormal(o.m_Normal);o.m_V1.addNormal(m_Normal);}
        else if (o.m_V1==m_V2){m_V2.addNormal(o.m_Normal);o.m_V1.addNormal(m_Normal);}

        if      (o.m_V2==m_V0){m_V0.addNormal(o.m_Normal);o.m_V2.addNormal(m_Normal);}
        else if (o.m_V2==m_V1){m_V1.addNormal(o.m_Normal);o.m_V2.addNormal(m_Normal);}
        else if (o.m_V2==m_V2){m_V2.addNormal(o.m_Normal);o.m_V2.addNormal(m_Normal);}

    }
    void Triangle::preCalc(){        
        // On a trois points $A$,$B$, et $C$ qui forment un plan 
        // avec une normale $\vec{n}.
        // On cree deux vecteurs $\vec{AB}$ et $\vec{AC}$. On a maintenant 
        // deux vecteurs sur un plan; à moins que $A$,$B$, et $C$ ne soient
        // alignés, tout va bien. 
        // Pour avoir un vecteur perpendiculaire au plan, 
        // il nous suffit de calculer le produit vectoriel des deux
        // vecteurs, donc, $\vec{n} = \vec{AB}\times\vec{AC}$.
        Vector AB = Vertex::vectorFromVertices(m_V0,m_V1);
        Vector AC = Vertex::vectorFromVertices(m_V0,m_V2);
        m_Normal = AB.cross(AC).normal();
        m_Normal.normalize();
        m_V0.addNormal(m_Normal);
        m_V1.addNormal(m_Normal);
        m_V2.addNormal(m_Normal);
        recalcNormals();

        m_uv0 = m_V0.getTextureUV();
        m_uvB = m_V1.getTextureUV() - m_uv0;
        m_uvC = m_V2.getTextureUV() - m_uv0;

        // AC' vecteur perpendiculaire à AC: n cross AC
        m_ACprime = m_Normal.cross(AC);
        // AB' vecteur perpendiculaire à AB: n cross AB
        m_ABprime = m_Normal.cross(AB);
        m_RhoB  = AB.dot(m_ACprime);
        m_RhoC  = AC.dot(m_ABprime);
    }
    bool Triangle::intersects(Ray& ray){
        float k = 10000000;
        return intersects(ray,k);
    }
    bool Triangle::intersects(Ray& ray, float& distance){
        /* Note: les produits vectoriels requierent 
         * "pas mal"(c'est relatif) de calcul, à
         * il serait donc p-ê pertinent de réduire
         * les équations avec un peu d'algèbre vectorielle*/
        /// Si le produit scalaire de deux vecteurs est nul           
        float kappa = ray.getDirection().dot(m_Normal);
        /// les deux vectors sont perpendiculaires, or la 
        /// normale du triangle est perpendiculaire au plan
        /// si kappa est nul, le rayon et le plan sont parallèles
        if (kappa==0.0f){
            /// On pourrait aussi vérifier que les deux ne sont
            /// pas confondus.
            return false;
        }
        /// rendu ici on sait qu'il y a une intersection avec le plan
        /// reste à savoir ou elle est, et ensuite
        /// si elle se trouve dans le triangle.
        /// On cherche theta(voir document de reference)
        /// qui est le coefficient d'intersection du rayon
        /// avec le triangle ( pt_intersection = theta*direction + origine)
        float theta = 
            (m_V0.getPosition()-ray.getPosition()).dot(m_Normal)/kappa;
         
        /// On ne veut pas non plus que le triangle soit derrière le rayon:
        if (theta<EPSILON){
            return false;
        }
        if (theta>distance){
            return false;
        }
        Vector n = ray.getDirection();
        /// on trouve maintenant le point P
        Vector P = ray.getDirection()*theta + ray.getPosition();
        /// on veut trouver les vecteurs AP,AB,AC
        Vector AP = P - m_V0.getPosition();
        /// on trouve maintenant les coefficients de composition
        /// lineaire de AP sur AB et AC, alpha et beta
        /// tels que AP = alpha*AB + beta*AC
        /// dans un triangle la somme des deux ne devrait pas 
        /// être supérieure à 1, et alpha et beta inférieurs à 0
        // valeurs precalculées:
        // AC' vecteur perpendiculaire à AC: n cross AC
        // AB' vecteur perpendiculaire à AB: n cross AB
        // ρB : AB.AC'
        // ρC : AC.AB' 
        float alpha = AP.dot(m_ACprime)/m_RhoB;
        float beta  = AP.dot(m_ABprime)/m_RhoC;
        float gamma = alpha+beta;
        if (alpha<0 || beta<0){
            return false;
        }
        if (gamma>1){
            return false;
        }
        /// tous les tests sont réussis, il y a intersection!
        distance = theta;
        return true;
    }

    void Triangle::getIntersectionParams(Math::Ray &ray,const float t, Vector *normal, Vector *uv){
        // on pas mal déjà tout quand on a la distance
        // on trouve alpha et beta qui sont les facteurs
        // de dépendance linéaire du vecteur 
        Vector P = ray.getDirection()*t + ray.getPosition();
        Vector AP = P - m_V0.getPosition();
        float alpha = AP.dot(m_ACprime)/m_RhoB;
        float beta  = AP.dot(m_ABprime)/m_RhoC;
        if (normal!=NULL){
			//*normal =  m_Normal;
			*normal = m_V0.getNormal() + m_nrB*alpha + m_nrC*beta;
        }
        if (uv!=NULL){   
            // vecteur uv:
            *uv = m_uv0 + m_uvB*alpha + m_uvC*beta;
        }
    }
}
